#include "stdafx.h"
#include "DataBus.h"
#include "Register/ACR.h"

struct TimeoutNotifier::Impl
{
	Impl()
	{
		m_hTimerQueue = ::CreateTimerQueue();
		if (m_hTimerQueue == NULL)
		{
			throw std::runtime_error("create timer queue failed");
		}
	}

	~Impl()
	{
		::DeleteTimerQueue(m_hTimerQueue);
	}

	struct TimerParameter
	{
		TimerParameter(Impl& in_impl, HANDLE in_hTimerQueue, UINT in_timeout, CallbackPtr in_callback)
			:impl(in_impl),hTimerQueue(in_hTimerQueue),hTimer(NULL),timeout(in_timeout),callback(in_callback)
		{
		}
		Impl& impl;
		HANDLE hTimerQueue;
		HANDLE hTimer;
		UINT timeout;
		CallbackPtr callback;

		void Release()
		{
			impl.RemoveTimeout(*this);
		}

		bool operator==(TimerParameter const& other) const
		{
			return &impl == &other.impl &&
				hTimerQueue == other.hTimerQueue &&
				hTimer == other.hTimer &&
				timeout == other.timeout &&
				callback == other.callback;
		}
		bool operator!=(TimerParameter const& other) const
		{
			return !operator==(other);
		}
	};

	static VOID CALLBACK TimerCallbackFunc(PVOID lpParam, BOOL TimerOrWaitFired)
	{
		UNREFERENCED_PARAMETER(TimerOrWaitFired);
		TimerParameter* parameter = (TimerParameter*)lpParam;
		parameter->callback->Callback();
		::DeleteTimerQueueTimer(parameter->hTimerQueue, parameter->hTimer, NULL);
	}

	bool AddTimeout(UINT timeout, CallbackPtr callback)
	{
		AutoCSLock autoLock(m_cs);
		m_timerParameters.push_back(TimerParameter(*this,m_hTimerQueue,timeout,callback));
		TimerParameter& parameter = m_timerParameters.back();
		BOOL ret = ::CreateTimerQueueTimer(&parameter.hTimer,m_hTimerQueue,(WAITORTIMERCALLBACK)TimerCallbackFunc,&parameter,timeout,0,WT_EXECUTEDEFAULT);
		return ret ? true : false;
	}

	void RemoveTimeout(TimerParameter& timerParameter)
	{
		AutoCSLock autoLock(m_cs);
		m_timerParameters.remove(timerParameter);
	}

	HANDLE m_hTimerQueue;
	CCriticalSection m_cs;
	std::list<TimerParameter> m_timerParameters;
};

TimeoutNotifier::TimeoutNotifier()
	:m_pImpl(new TimeoutNotifier::Impl())
{
}

TimeoutNotifier::~TimeoutNotifier()
{
}

bool TimeoutNotifier::AddTimeoutCallback(UINT timeout, CallbackPtr callback)
{
	return m_pImpl->AddTimeout(timeout, callback);
}